﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////


#pragma once
#include "GnufItem.h"
#include "GnufSearchBox.h"

namespace gnuf {
    class QtTreeBoard;
    class QtTreeItem;
    class Toolbar;
    class TreeNode;
    class Info;
    class CustomizedDelegateBase;

    class GNUF_BASE_EXPORT TreeBoard final
        : public ItemWith<QtTreeBoard>
        , public SearchDataModel
    {
        CANNOT_COPY_OR_ASSIGN(TreeBoard)
    public:
        static const EItemType Type;
        explicit TreeBoard(Item *);
        virtual ~TreeBoard();
        enum class SelectionMode
        {
            SingleSelection = 1,
            MultiSelection = 2
        };

    public:
        TreeNode* addTree();
        TreeNode* insertTreeBefore(TreeNode*);
        typedef std::list< gcmp::OwnerPtr< TreeNode > > Trees;
        typedef std::vector< TreeNode * > RawTrees;

        inline const Trees & trees() const { return m_trees; }
        RawTrees rawTrees();

        // The current tree node just is the one that the user selects.
        // The hovered tree node just is the one over which the mouse is.

        void setCurrentNode(TreeNode* node,bool callBack = true);
        const TreeNode * currentNode() const;
        TreeNode * currentNode();
        void onCurrentNodeClicked(TreeNode* node);

        void setHoveredNode(TreeNode *);
        const TreeNode * hoveredNode() const;
        TreeNode * hoveredNode();

        void setSelectionMode(SelectionMode mode);
        SelectionMode selectionMode();
        void deleteTree(TreeNode* node);
        void expandAll();
        void collapseAll();
        void clear();
        void refreshIndex();

        // 相对于expandAll， 只刷新item，保持展开状态，
        void refreshGeometries();

        void setVisible(bool);
        bool isVisible() const;

        void setColEnabled(int col, bool bEnable) { m_mapColEnabled[col] = bEnable; }
        bool colEnabled(int col) {
            auto iter = m_mapColEnabled.find(col);
            return iter == m_mapColEnabled.end() ? true : iter->second;
        }

        void enableSpanFirstColumnWhenAnotherColumnsInvisiblePerRow(bool val)
        {
            m_isSpanFirstColumnPerRow = val;
        }
        inline bool isSpanFirstColumnWhenAnotherColumnsInvisiblePerRow() const { return m_isSpanFirstColumnPerRow; }

        void setEnableHovered(bool has);
        bool enabledHover() const { return m_enalbedHover; }

        void toString(bool pre, std::wstring& res) const;

        ///\brief Set the delegate for the node.
        ///\param pTreeNode: the tree node on which set the deletate.
        ///\param pCustomizedDelegate: the customized delegate.
        ///\param wantToTransferOwnership: if true, the QAbstractItemDelegate that 
        ///       the pCustomizedDelegate provides will be deleted by the QtTreeBoard.
        bool setCustomizedDelegate(
            TreeNode * pTreeNode,
            CustomizedDelegateBase * pCustomizedDelegate,
            const bool wantToTransferOwnership);

    protected:
        virtual const std::list<std::wstring> search(const std::wstring&) override;

    public:
        bool hasLinkline();
        void setHasLinkLine(bool bHas);

    protected:
        // Inherits from ItemWith...
        virtual void onDisconnectFromMyQtItem() override;
        virtual QtItem* getQtItem() const override;

        // Inherits from Item
        virtual void onPostAppearanceSettingChanged() override;

    private:
        Trees m_trees;
        gcmp::WeakPtr<TreeNode> m_pCurrentNode;
        gcmp::WeakPtr<TreeNode> m_pHoveredNode;
        std::map<int, bool> m_mapColEnabled;
        bool m_enalbedHover;
        bool m_hasLinkLine;
        bool m_isSpanFirstColumnPerRow;
        SelectionMode m_selectionMode = SelectionMode::SingleSelection;
    };

    class GNUF_BASE_EXPORT TreeNodeSorter : public gcmp::WeakReferenceable
    {
    public:
        virtual ~TreeNodeSorter() {}

        // Interface of comparing two tree nodes, return true as left node less than the right, otherwise, return false.
        bool compare(const TreeNode* left,const TreeNode* right);

    protected:
        virtual bool compareImpl(const TreeNode*, const TreeNode*);
    };

    class GNUF_BASE_EXPORT TreeNode final
        : public ItemWith<QtTreeItem>
        , public WithUserDataMap
    {
        friend class TreeBoard;
        friend class QtTreeItem;

        typedef std::list< gcmp::OwnerPtr< TreeNode > > TreeNodes;

    public:
        virtual ~TreeNode();

        enum class NodeType : std::uint8_t
        {
            eText,
            eTextEditor,    // 暂不支持
            eCheckBox, 
            eButton,        // 暂不支持
            eList           // 暂不支持
        };

    public:
        static const EItemType Type;

        bool isRoot() const { return nullptr == m_parentNode; }
        TreeNode * parentNode() const { return m_parentNode; }

        TreeNode* addSubNode();
        inline const TreeNodes & subNodes() const { return m_subNodes; }
        std::vector<TreeNode *> rawSubNodes();

        // if this is a root node, return nullptr
        TreeNode* addPreSiblingNode();
        TreeNode* addNextSiblingNode();
        TreeNode* nextSiblingNode();
        TreeNode* preSiblingNodes();
        void deleteSelf();
        void deleteSubNode(TreeNode *);
        void clear();

        void sort(bool bAsc);
        void setSorter(TreeNodeSorter* sorter);
        inline TreeNodeSorter* getSorter() { return m_wpSorter.Get(); }
        inline const TreeNodeSorter* getSorter() const { return m_wpSorter.Get(); }

        TreeNode* findSubItem(int index, const std::wstring& caption);
        void toString(bool ,std::wstring& str) const;

        void setGeometry(int x, int y, int width, int height);
        const gnuf::Rectangle geometry() const { return m_rect; }
        gnuf::Rectangle & rGeometry() { return m_rect; }

    public:
        void setEnable(bool bEnable);
        bool enabled() const;

        void setSelect(bool bSelect);
        bool isSelected() const;

        void setActive(bool bActive);
        bool isActived() const;

        void setExpanded(bool expand);
        bool isExpanded() const;

        void setVisible(bool);
        bool isVisible() const;

        void setSelectable(bool);

        void setWantShowIcon(bool bWantShowIcon) { m_wantShowIcon = bWantShowIcon; }
        bool wantShowIcon() const { return m_wantShowIcon; }

        inline bool isLeaf() const { return m_subNodes.empty(); }

        void setInfo(int index, const Info& info, const std::wstring& pixmap = L"");
        void setInfo(int index, const Info& info, TreeNode::NodeType type, const std::wstring& pixmap = L"");
        void setInfo(int index, const Info& info, TreeNode::NodeType type, CheckStatus status, const std::wstring& pixmap = L"");

        void setHoveredInfo(const Info& info);
        const Info* hoveredInfo() const { return m_hoveredInfo.get(); }

        const Info* nodeInfo(int index) const;
        TreeNode::NodeType nodeType(int index) const;

        Item* nodeItem(int index);
        const Item* nodeItem(int index) const;

        void setCheckStatus(int index, CheckStatus status);
        CheckStatus checkStatus(int index) const;

        void enabledDrag(bool);

        inline bool isDragEnabled() const {
            return m_enabledDrag;
        }

        int column() const { 
            return m_columnNum; 
        }

        void setColEnabled(int col, bool bEnable) { m_mapColEnabled[col] = bEnable; }
        bool colEnabled(int col) const {
            auto iter = m_mapColEnabled.find(col);
            return iter == m_mapColEnabled.end() ? true : iter->second;
        }

        std::wstring getNodePath();
    protected:
        // Inherits from ItemWith...
        virtual void onDisconnectFromMyQtItem() override;
        virtual void onPostAppearanceSettingChanged() override;

    protected:
        explicit TreeNode(TreeBoard*, bool isInsert = false);
        explicit TreeNode(TreeNode*, bool isInsert = false);

    private:
        TreeNode* addSubNodeBefore(TreeNode* node);
        TreeNode* addSubNodeAfter(TreeNode* node);
        TreeNode* subNodeBefore(TreeNode* node);
        TreeNode* subNodeAfter(TreeNode* node);

    private:
        struct NodeInfo
        {
            gcmp::OwnerPtr<Info> info;
            std::wstring pixmapStr;
            NodeType type = NodeType::eText;
            CheckStatus checkStatus = CheckStatus::Checked;
        };

    private:
        TreeNode * m_parentNode;
        TreeNodes m_subNodes;
        std::wstring m_text;

        std::map<int, NodeInfo> m_nodeInfo;
        std::map<int, gcmp::OwnerPtr<Item>> m_nodeItem;

        bool m_enabledDrag;
        bool m_wantShowIcon;
        int m_columnNum;
        std::map<int, bool> m_mapColEnabled;
        gcmp::WeakPtr<TreeNodeSorter> m_wpSorter;

        gcmp::OwnerPtr<Info> m_hoveredInfo;
        gnuf::Rectangle m_rect;
        CANNOT_COPY_OR_ASSIGN(TreeNode)
    };
}
